﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Proggy.Data.Abstract;
using System.Threading;
using Proggy.Web.Cms.Repositories;
using Proggy.Web.Cms.Repositories.Published;
using Proggy.Web.Cms.Repositories.ContentModel;
using System.Web;
using Proggy.Web.Cms.Context;
using System.Threading.Tasks;
using Proggy.Data.Concrete;
using Proggy.Web.Cms.Repositories.Membership;
using Proggy.Web.Cms.Service;
using Proggy.Web.Cms.ContentModel;

namespace Proggy.Web.Cms.Cache
{
    /// <summary>
    /// Manages vm cache
    /// </summary>
    public class CachedContentManager
    {
        /// <summary>
        /// Sets the cache of IPublishedContentRepositories
        /// </summary>
        public static void SetCache(IContentModelTypeRepository typeRepo, IContentModelConverter conv)
        {
            setCache(typeRepo, conv);
        }

        /// <summary>
        /// Sets the cache of IPublishedContentRepositories
        /// </summary>
        /// <param name="maxSizeOfCache">The maximum number of IPublishedContentRepository instances to hold in the cache</param>
        public static void SetCache(IContentModelTypeRepository typeRepo, IContentModelConverter conv, int maxSizeOfCache)
        {
            _maxSizeOfCache = maxSizeOfCache;
            setCache(typeRepo,conv);
        }

        /// <summary>
        /// set cache
        /// </summary>
        private static void setCache(IContentModelTypeRepository typeRepo, IContentModelConverter conv)
        {
            _cmtRepo = typeRepo;
            _conv = conv;
            instantiateRepos();
            addToRepos();
        }

        /// <summary>
        /// Add repo to repos collection
        /// </summary>
        /// <param name="repo"></param>
        /// <param name="maxSizeOfCache"></param>
        private static void addToRepos()
        {
            var repo = getNewRepo();
            var thread = new Thread(() =>
            {
                for (int i = 0; i < _maxSizeOfCache; i++)
                {
                    _repos.Add(repo);
                    Thread.Sleep(50);
                }
            });
            thread.Start();
        }

        /// <summary>
        /// Instantiate repos
        /// </summary>
        /// <param name="maxSizeOfCache"></param>
        private static void instantiateRepos()
        {
            _repos = new List<IPublishedContentRepository>(_maxSizeOfCache);
        }

        /// <summary>
        /// Gets an IPublishedContentRepository instance
        /// </summary>
        /// <param name="checkFrontEndRequestContext">If true, will check front end request context first</param>
        /// <returns></returns>
        public static IPublishedContentRepository GetRepositoryInstance(bool checkFrontEndRequestContext)
        {
            // try getting from front end context
            if (
                checkFrontEndRequestContext &&
                FrontEndRequestContext.Current != null &&
                FrontEndRequestContext.Current.CurrentCachedContentRepository != null)
            {
                return FrontEndRequestContext.Current.CurrentCachedContentRepository;
            }
            else
            {
                // max attempts
                var maxAttempts = 100;
                var currentAttempts = 0;

                // try to get instance
                return getRepositoryInstance(currentAttempts, maxAttempts);
            }
        }

        /// <summary>
        /// Recursive function
        /// </summary>
        /// <param name="currentAttempts"></param>
        /// <param name="maxAttempts"></param>
        /// <returns></returns>
        private static IPublishedContentRepository getRepositoryInstance(int currentAttempts, int maxAttempts)
        {
            // check
            if (_repos.Count < 1)
            {
                currentAttempts++;
                if (currentAttempts < maxAttempts)
                {
                    Thread.Sleep(100);
                    getRepositoryInstance(maxAttempts, currentAttempts);
                }
                else
                {
                    if (FallBackInstance == null)
                    {
                        throw new Exception("Error in retrieving instance of cached content repository. Set CachedContentManager.FallbackInstance");
                    }
                    else
                    {
                        return FallBackInstance;
                    }
                }
            }

            // get random
            var indexToGet = _random.Next(0, _repos.Count);

            // return
            return _repos[indexToGet];
        }

        /// <summary>
        /// Fallback instance - set for testing purposes
        /// </summary>
        public static IPublishedContentRepository FallBackInstance { get; set; }

        /// <summary>
        /// Returns a new repo
        /// </summary>
        /// <returns></returns>
        private static IPublishedContentRepository getNewRepo()
        {
            var repo = new PublishedContentRepository(_cmtRepo, _conv);
            return repo;
        }

        /// <summary>
        /// The repos
        /// </summary>
        private static List<IPublishedContentRepository> _repos;

        /// <summary>
        /// A random generator
        /// </summary>
        private static Random _random = new Random();

        /// <summary>
        /// Max size of cache (100 default)
        /// </summary>
        private static int _maxSizeOfCache = 100;

        private static IContentModelTypeRepository _cmtRepo;

        private static IContentModelConverter _conv;
    }
}
